home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
C/C++ Users Group Library 1996 July
/
C-C++ Users Group Library July 1996.iso
/
vol_400
/
429_01
/
kbfake10
/
kbfake.c
< prev
next >
Wrap
C/C++ Source or Header
|
1994-04-06
|
7KB
|
205 lines
/* KBFAKE.C
This program is a Terminate and Stay Resident program that will
redirect the input from one serial line so that it looks like it has
been typed on the keyboard. It works by installing an interrupt handler
for the serial port that is to be used. This handler inserts the
characters coming in over the serial line into the keyboard buffer using
the BIOS keyboard write call.
The code for this program is a mixture of the example for the
keep() function call in Borland C and various programming examples from
the SIMTEL internet FTP site.
This is an interrupt service routine. You can NOT compile this
program with Test Stack Overflow turned on and get an executable file
which will operate correctly. */
#include <stdlib.h>
#include <stdio.h>
#include <bios.h> // bioscom() declared here
#include <dos.h> // keep() is declared here
// Reduce heaplength and stacklength to make a smaller program in memory
extern unsigned _heaplen = 256;
extern unsigned _stklen = 1024;
const unsigned BIOSdseg = 0x0040; // BIOS data segment
const unsigned IMR = 0x21; // Interrupt Mask Register
const unsigned safety_space = 1024; // Bytes of spare space
// These are offsets from the UART base port
const unsigned UART_THR = 0x00; // Transmit Hold Register and
const unsigned UART_RBR = 0x00; // Receiver Buffer Register
const unsigned UART_IER = 0x01; // Interrupt Enable Register
const unsigned UART_IIR = 0x02;
const unsigned UART_LCR = 0x03; // Line Control Register?
const unsigned UART_MCR = 0x04; // Modem Control Register?
const unsigned UART_LSR = 0x05; // Line Status Register
const unsigned UART_MSR = 0x06;
//-----------------------------------------------------------------
// Global variables needed both during initialization and interrupt
// processing.
//-----------------------------------------------------------------
int base_port; // Base I/O port address
//-----------------------------------------------------------------
// Handle the incoming data interrupt from the serial port.
// Read the incoming character and add it to the BIOS keyboard
// buffer.
// Note: Interrupts are disabled by the processor as part of
// the interrupt processing, so we do not need to disable them by
// hand. We also don't need to enable interrupts before leaving,
// since the FLAGS register is popped from the stack upon IRET,
// restoring the interrupt control flag.
//-----------------------------------------------------------------
void interrupt serial_input_handler(void) {
static unsigned char in_char;
static unsigned char scancode;
// Get the character from the comm port RBR register
in_char = inportb(base_port + UART_RBR);
// Set the keyboard scan code to zero, which is what would
// happen if they had used the alt-numpad method to enter
// the character, rather than pressing the key. This should
// not be noticed by keyboard-reading software, and saves
// having to keep a table of key scan codes for each ASCII
// code.
scancode = 0;
// Write the character to the keyboard buffer using BIOS
// Use BIOS call 0x16 (Keyboard services)
// AH = service to use = 0x05 (keyboard write)
// CL = ASCII code of character
// CH = Keyboard scan code of character
_AH = 0x05;
_CL = in_char;
_CH = scancode;
geninterrupt(0x16);
// Tell the 8259A End Of Interrupt
outportb(0x20,0x20);
}
void Usage(char *s) {
fprintf(stderr,"Usage: %s [port]\n",s);
fprintf(stderr," port: Serial COMM port to read (1-4)\n");
fprintf(stderr," (default = 1)\n");
exit(-1);
}
void main(unsigned argc, char *argv[]) {
int port = 1; // COMM port to read (1-4)
int temp; // Temporary holding register
int int_to_grab; // Which interrupt vector to grab
int int_to_enable; // Which hardware interrupt to enable
// Parse the command line
switch (argc) {
case 1: // No arguments
break;
case 2: // COM port number (1-4) as argument
port = atoi(argv[1]);
if ( (port < 1) || (port > 4) ) Usage(argv[0]);
break;
default:
Usage(argv[0]);
}
// Find the interrupt to use based on the port selected
// For some reason, the interrupt you grab the vector for is
// not the same as the hardware interrupt you enable.
switch (port) {
case 1:
case 3:
int_to_grab = 0x0c;
int_to_enable = 4;
break;
case 2:
case 4:
int_to_grab = 0x0b;
int_to_enable = 3;
break;
default:
fprintf(stderr,"Internal error 1 - port is %d\n",port);
exit(-1);
}
// Find the I/O base address based on the port selected
// This information is read from the BIOS data segment that
// is at segment 0x0040. The first four words hold the
// I/O base addresses for the first four com ports.
base_port = *(int far *)MK_FP(BIOSdseg,(port-1)*2);
if (base_port == 0) {
fprintf(stderr,"COM%d has no BIOS base port\n",port);
fprintf(stderr," (KBFAKE not installed)\n");
exit(-1);
}
// Tell what we're doing
printf("KBFAKE: Installing on port %d (vector %2X, interrupt %d, I/O base %4X)\n",
port, int_to_grab, int_to_enable, base_port);
// Set the parameters on the COM port to:
// 0xE0: 9600 Baud
// 0x03: 8 Data bits (0x02 = 7 data bits)
// 0x00: 1 Stop bit (0x04 = 2 stop bits)
// 0x00: No parity (0x08 = odd, 0x18 = even)
(void)bioscom(0, 0xE0 | 0x03 | 0x00 | 0x00 , port-1);
printf(" 9600 baud, 8 data bits, 1 stop bit, no parity\n");
/* install the new interrupt handler for serial input */
setvect(int_to_grab, serial_input_handler);
// Disable interrupts while we are messing with the UART
disable();
//-----------------------------------------------------------------
// Program the UART for the serial port we are using
//-----------------------------------------------------------------
// Turn off the Divisor Access Latch Bit to allow access to RBR, etc.
temp = inportb(base_port + UART_LCR) & 0x7F; // Strip upper bit
outportb(base_port + UART_LCR, temp);
// Assert DTR, RTS, and OUT2 on the COM port
outportb(base_port + UART_MCR, 0x0B);
// Enable interrupts for data ready on the serial line (8250)
outportb(base_port + UART_IER, 0x01);
// Read the Line Status Register to clear any reported errors
temp = inportb(base_port + UART_LSR);
// Read the Receiver Buffer Register to clear any incoming character
temp = inportb(base_port + UART_RBR);
//-----------------------------------------------------------------
// Enable the IRQ for the serial line on the 8259A interrupt control
// Do this by clearing the bit in the ISR corresponding to the
// interrupt that is to be enabled.
//-----------------------------------------------------------------
temp = inportb(IMR); // Read the value of the Interrupt Mask Reg
temp &= ~(1 << int_to_enable); // Zero bit for one to enable
outportb(IMR,temp); // Send the value back
// Re-enable interrupts, since all is set up
enable();
// Terminate and Stay Resident
/* _psp is the starting address of the
program in memory. The top of the stack
is the end of the program. Using _SS and
_SP together we can get the end of the
stack. You may want to allow a bit of
safety space to insure that enough room
is being allocated ie:
(_SS + ((_SP + safety space)/16) - _psp)
*/
keep(0,(_SS+( (_SP+safety_space)/16)-_psp));
}